# -*- coding: UTF-8 -*-
from mutator import blobmuta
from mutator import stringmuta
from mutator import binarymuta
import random
import bluetooth


class Obex:

    # connect operation (request)
    CONN_OPCODE = '\x80' # 1byte
    CONNECT_REQUEST_PACKET_LENGTH = '' # 2bytes
    OBEX_VERSION_NUMBER = '' # 1byte
    FLAGS = '' #  1byte
    MAXIMUM_OBEX_PACKET_LENGTH = '' # 2bytes
    OPTIONAL_HEADERS = ''
    # ---------------------------------------------------------------------------
    # disconnect operation (request)
    DISCONN_OPCODE = '\x81'
    PACKET_LENGTH = '\x00\x05' # 2bytes
    D_OPTIONAL_HEADERS = ''
    # ---------------------------------------------------------------------------
    # put operation
    PUT_OPCODE = '\x82'
    PUT_PACKET_LENGTH = '\x00\x06'
    SEQUENCE_OF_HEADERS = '\x01'
    # ---------------------------------------------------------------------------
    # get operation
    GET_OPCODE = '\x03'
    GET_PACKET_LENGTH = '\x00\x05'

    def __init__(self, addr):
        self.addr = addr

    def fuzz_get_request(self):
        opcode = '\x03'
        length = '\x00\x01'
        playload = '\x81' * 5
        r_code = random.randint(0, 1)
        rand = random.Random()
        r_flag = random.randint(1, 12)
        if r_code == 0:
            playload = self._fs(playload, rand, r_flag)
        msg = opcode + length + playload
        if r_code == 1:
            msg = self._fs(msg, rand, r_flag)
        network_code = self.send_obex(msg)
        return [network_code, r_flag, msg]
        # return [network_code, r_code, r_flag, msg, opcode, length, playload]

    def fuzz_put_request(self):
        opcode = '\x82'
        length = '\x00\x00'
        playload = '\x81' * 5
        r_code = random.randint(0, 1)
        rand = random.Random()
        r_flag = random.randint(1, 12)
        if r_code == 0:
            playload = self._fs(playload, rand, r_flag)
        msg = opcode + length + playload
        if r_code == 1:
            msg = self._fs(msg, rand, r_flag)
        network_code = self.send_obex(msg)
        return [network_code, r_flag, msg]
        # return [network_code, r_code, r_flag, msg, opcode, length, playload]

    def fuzz_disconnect_request(self):
        opcode = '\x81'
        length = '\x00\x05'
        optional_headers = '\x80' * 5
        r_code = random.randint(0, 1)
        rand = random.Random()
        r_flag = random.randint(1, 12)
        if r_code == 0:
            optional_headers = self._fs(optional_headers, rand, r_flag)
        msg = opcode + length + optional_headers
        if r_code == 1:
            msg = self._fs(msg, rand, r_flag)
        network_code = self.send_obex(msg)
        return [network_code, r_flag, msg]
        # return [network_code, r_code, r_flag, msg, opcode, length, optional_headers]

    def fuzz_connect_request(self):
        opcode = '\x80'
        length = '\x00\x07'
        version = '\x10'
        flags = '\x01'
        maximum_length = '\x00\x10'
        optional_headers = '\x81' * 5
        r_code = random.randint(0, 3)
        rand = random.Random()
        r_flag = random.randint(1, 12)
        if r_code == 0:
            length = self._fs(length, rand, r_flag)
        if r_code == 1:
            maximum_length = self._fs(maximum_length, rand, r_flag)
        if r_code == 2:
            optional_headers = self._fs(optional_headers, rand, r_flag)
        msg = opcode + length + version + flags + maximum_length + optional_headers
        if r_code == 3:
            msg = self._fs(msg, rand, r_flag)
        network_code = self.send_obex(msg)
        return [network_code, r_flag, msg]
        # return [network_code, r_code, r_flag, msg, opcode, length, version, flags, maximum_length, optional_headers]

    # return [network_code, r_flag, msg]
    def obex_random_fuzz(self):
        i = random.randint(0, 3)
        if i == 0:
            return self.fuzz_get_request()
        if i == 1:
            return self.fuzz_put_request()
        if i == 2:
            return self.fuzz_connect_request()
        if i == 3:
            return self.fuzz_disconnect_request()


    def send_obex(self, msg):
        psm = 8 # 0x0008
        sock = bluetooth.BluetoothSocket(bluetooth.L2CAP)
        sock.settimeout(5)
        try:
            sock.connect((self.addr, psm))
        except:
            return 1
        try:
            # 同一个包发5次
            for i in range(0, 5):
                sock.send(msg)
        except:
            return 2
        return 0

    def _fs(self, data, rand, flag):

        if (flag == 0):
            return data

        if (flag == 1):
            s = stringmuta.StringCaseMutator()
            return s.mutationRandomCase(data)

        if (flag == 2):
            s = stringmuta.UnicodeBomMutator()
            return s.randomMutation(rand)

        if (flag == 3):
            s = stringmuta.UnicodeBadUtf8Mutator()
            return s.randomMutation(rand)

        if (flag == 4):
            b = blobmuta.DWORDSliderMutator(data)
            return b.randomMutation(data, rand)

        if (flag == 5):
            b = blobmuta.BitFlipperMutator(data)
            return b.randomMutation(data, rand)

        b = blobmuta.BlobMutator(data)
        if (flag == 6):
            return ""  # 返回空字符串

        if (flag == 7):
            return b.changeReduceBuffer(data, rand)

        if (flag == 8):
            return b.changeChangeRangeSpecial(data, rand)

        if (flag == 9):
            return b.changeExpandBuffer(data, rand)

        if (flag == 10):
            return b.changeNullRange(data, rand)

        if (flag == 11):
            return b.changeChangeRange(data, rand)

        if (flag == 12):
            h = binarymuta.Handler()
            return h.Fuzzit(data)

